﻿namespace WDE.Helpers
{
    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.Drawing;
    using System.Linq;
    using System.Threading;
    using OpenQA.Selenium;
    using OpenQA.Selenium.Remote;
    using Properties;

    public static class DomHelper
    {
        #region Extension ITargetLocator

        /// <summary>
        /// Метод ищет диалоговое окно
        /// </summary>
        /// <param name="locator"></param>
        /// <param name="driver"></param>
        /// <returns>Возвращает ExtWinDlg</returns>
        public static ExtWinDlg ExtAlert(this ITargetLocator locator, IWebDriver driver)
        {
            return new ExtWinDlg(FindByJQuery<Element>(driver, Framework.jQuery.By.JQuery(".x-window-dlg")));
        }
        /// <summary>
        /// Метод ищет фрейм 
        /// </summary>
        /// <param name="locator"></param>
        /// <param name="driver"></param>
        /// <returns>Возвращает IWebDriver</returns>
        public static IWebDriver Frame(this ITargetLocator locator, IWebDriver driver)
        {
            var step = 0;
            InjectJavaScriptResource(driver);
            while (step < 50)
            {
                WaitAjax(driver);
                try
                {
                    return driver.SwitchTo().Frame(0);
                }
                catch (Exception)
                {
                }

                Thread.Sleep(100);
                step++;
            }

            return driver;
        }

        public static void GoToUrl(this INavigation navigat, IWebDriver driver, string url)
        {
            driver.Navigate().GoToUrl(url);
            driver.Sleep(200);
            driver.WaitAjax();
        }

        #endregion

        #region Extension IWebDriver
        /// <summary>
        /// Выполнение скрипта обернутое Try{} catch{}
        /// </summary>
        /// <param name="driver"></param>
        /// <param name="code"></param>
        /// <param name="args"></param>
        /// <returns>Возвращает object</returns>
        public static object ExcuteJs(this IWebDriver driver, string code, params object[] args)
        {
            object result = null;

            try
            {
                result = ((IJavaScriptExecutor)driver).ExecuteScript(code, args);
            }

            catch (Exception)
            {

            }

            return result;
        }

        /// <summary>
        /// Задаёт ожидание в миллисекундах
        /// </summary>
        /// <param name="driver">текущий драйвер</param>
        /// <param name="millisecond">Время паузы в миллисекундах</param>
        public static void Sleep(this IWebDriver driver, int millisecond)
        {
            Thread.Sleep(millisecond);
        }

        public static T Find<T>(this IWebDriver driver, Func<IWebDriver, IWebElement> func, params object[] args) where T : Element
        {
            var step = 0;

            while (step < 50)
            {
                WaitAjax(driver);
                try
                {
                    var result = func(driver);

                    if (result != null)
                    {
                        var type = typeof (T);
                        var constructorInfo = type.GetConstructors()[0];
                        var constructorArgs = new object[] {result, driver};

                        if (constructorInfo != null)
                        {
                            return (T) constructorInfo.Invoke(constructorArgs.Concat(args).ToArray());
                        }
                    }
                }
                catch (Exception)
                {
                }


                Thread.Sleep(100);
                step++;
            }

            return null;
        }

        /// <summary>
        /// Метод поиска с ожиданием вовращает "T" которое наследуется от Element
        /// Поиск осуществляется с помощью стандартных средств WebDriver
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="driver"></param>
        /// <param name="by"></param>
        /// <param name="element"></param>
        /// <param name="args"></param>
        /// <returns>Возвращает "T"</returns>
        public static T FindBy<T>(this IWebDriver driver, By by, IWebElement element = null, params object[] args) where T : Element
        {
            return driver.Find<T>(x =>
            {
                var el = element == null ? x.FindElement(@by) : element.FindElement(@by);

                return el.Size == Size.Empty ? null : el;
            }, args);
        }

        /// <summary>
        /// Метод поиска с ожиданием вовращает "T" которое наследуется от Element
        /// Поиск осуществляется с помощью jQueryCssSelector
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="driver"></param>
        /// <param name="by"></param>
        /// <param name="element"></param>
        /// <param name="args"></param>
        /// <returns>Возвращает "T"</returns>
        public static T FindByJQuery<T>(this IWebDriver driver, Framework.jQuery.By.JQueryBy by, IWebElement element = null, params object[] args) where T : Element
        {
            var step = 0;

            while (step < 500)
            {
                try
                {
                    return driver.Find<T>(x =>
                    {
                        IWebElement el;

                        if (element == null)
                        {
                            el = Framework.jQuery.SeleniumExtensions.FindElement((RemoteWebDriver)driver, by);
                        }
                        else
                        {
                            var id = element.GetAttribute("id");

                            if (String.IsNullOrEmpty(id))
                            {
                                id = Guid.NewGuid().ToString();
                                driver.ExcuteJs("$(arguments[0]).attr('id', arguments[1]);", element, id);
                            }

                            el = Framework.jQuery.SeleniumExtensions.FindElement((RemoteWebDriver)driver,
                                    Framework.jQuery.By.JQuery("#" + id + "\").find" + by.Selector.Substring(0, by.Selector.Length - 2)));
                        }

                        return el.Size == Size.Empty ? null : el;
                    }, args);
                }
                catch (Exception)
                {
                }

                driver.Sleep(100);
                step++;
            }

            return null;
        }

        public static bool TryCompileForm(this IWebDriver driver, ref string stack)
        {
            if (stack == null) throw new ArgumentNullException("stack");
            var ria = (bool)driver.ExcuteJs(@"
                        try {
                                RIA; 
                                return true;
                            } 
                        catch(e)
                            {
                                return false;
                            };");
            var riaForms = (bool)driver.ExcuteJs(@"
                            try {
                                RIA.Forms; 
                                return true;
                                } 
                            catch(e)
                                {
                                return false;
                                }");
            var compileErrorMessage = (string)driver.ExcuteJs(@" var compileErrorMessage = RIA.Forms.Utils.getFormViewport().CompileErrorMessage; return compileErrorMessage || '';");
            var step = 0;
            string messege;
            var all = new List<string>();
            while (step < 50)
            {
                try
                {
                    if (ria)
                    {
                        if (riaForms)
                        {
                            if (String.IsNullOrEmpty(compileErrorMessage))
                            {
                                stack = "Ошибок нет";
                                return true;
                            }
                            stack = compileErrorMessage;
                            return false;
                        }
                    }
                    else
                    {
                        stack = "Ria не найден";
                        return false;
                    }


                    driver.Sleep(100);
                    step++;
                }
                catch (Exception e)
                {
                    messege = e.Message;
                    all.Add(messege);
                    stack = e.Message;
                    return false;
                }
            }
            stack = "Ошибок нет";
            return true;
        }

        public static void FiltrColumnByLable(this IWebDriver driver, string lable,string value)
        {
            var lableIsTwoWord = lable.Split(' ');
            var el =driver.FindByJQuery<Element>(lableIsTwoWord.LongLength == 2 ? Framework.jQuery.By.JQuery(".x-grid3-hd-inner:contains(" + lableIsTwoWord[0] + "):contains(" + lableIsTwoWord[1] + ")").Parent().Find(".x-form-field") : Framework.jQuery.By.JQuery(".x-grid3-hd-inner:contains(" + lable + ")").Parent().Find(".x-form-field"));
            el.El.Clear();
            el.El.SendKeys(value);
            el = driver.FindByJQuery<Element>(lableIsTwoWord.LongLength == 2 ? Framework.jQuery.By.JQuery(".x-grid3-hd-inner:contains(" + lableIsTwoWord[0] + "):contains(" + lableIsTwoWord[1] + ")") : Framework.jQuery.By.JQuery(".x-grid3-hd-inner:contains(" + lable + ")"));
            el.Click();
        }

        /// <summary>
        /// Инъекция JavaScrpt файла в тело приложения.
        /// </summary>
        /// <param name="driver"></param>
        public static void InjectJavaScriptResource(this IWebDriver driver)
        {
            var step = 0;
            var js = Resources.help;
            var jsmin = js.Replace("\r\n", "");
            while (step < 1000)
            {
                try
                {
                    if (((IJavaScriptExecutor)driver).ExecuteScript("return window.Ext.version") != null)
                        ((IJavaScriptExecutor)driver).ExecuteScript(jsmin);
                    return;
                }
                catch
                {
                    Sleep(driver, 10);
                    step++;
                }
            }
            throw new Exception("Не удалось загрузить файл JS");
        }

        /// <summary>
        /// Ожидание завершения работы Ajax
        /// </summary>
        /// <param name="driver"></param>
        private static void WaitAjax(this IWebDriver driver)
        {
            var step = 0;
            Sleep(driver, 200);
            while (step < 50)
            {
                var jsInt = Convert.ToInt32(driver.ExcuteJs(@"return window.Ext.Test.CountRequestComplete"));
                if (jsInt == 0)
                {
                    return;
                }
                Sleep(driver, 200);
                step++;
            }
        }

        /// <summary>
        /// проверка ошибок яваскрипта и серверных ошибок
        /// </summary>
        /// <param name="driver"></param>
        /// <returns>Класс с методом печати в консоль</returns>
        public static Report CheckError(this IWebDriver driver)
        {
            var clienterror = driver.ExcuteJs(@"return Ext.Test.ClientError;") as ReadOnlyCollection<object>;
            driver.WaitAjax();
            var serverError = driver.ExcuteJs(@"return Ext.Test.ServerError;") as ReadOnlyCollection<object>;
            driver.WaitAjax();
            return new Report(serverError, clienterror);
        }
        #endregion

        public static Element XFind(this IWebDriver driver, Xtype xtype)
        {
            if (xtype == Xtype.FIRSTROW || xtype == Xtype.WINDOW)
            {
                return driver.FindByJQuery<Element>(Framework.jQuery.By.JQuery(xtype.Name + ":visible()"));
            }
            return XFind(driver, xtype, null);
        }

        public static Element XFind(this IWebDriver driver, Xtype xtype, string text)
        {
            if (xtype == Xtype.PANEL ||
                xtype == Xtype.WINDOW ||
                xtype == Xtype.BUTTON ||
                xtype == Xtype.FIRSTROW ||
                xtype == Xtype.EASMENU ||
                xtype == Xtype.EASMENUSUBITEM ||
                xtype == Xtype.MENUSUBITEMCONTAINER ||
                xtype == Xtype.MENUITEM ||
                xtype == Xtype.COMBOLISTITEM ||
                xtype == Xtype.TABPANEL                )
            {
                return
                    driver.FindByJQuery<Element>(String.IsNullOrEmpty(text)
                        ? Framework.jQuery.By.JQuery(xtype.Name + ":visible()")
                        : Framework.jQuery.By.JQuery(xtype.Name + ":contains(" + text + "):visible()"));
            }
            else
            {
                //if (xtype == Xtype.DATEPICKER ||
                //    xtype == Xtype.DATEFIELD ||
                //    xtype == Xtype.DICTIONARY ||
                //    xtype == Xtype.TEXTFIELD ||
                //    xtype == Xtype.COMBOBOX ||
                //    xtype == Xtype.CHECKBOX)

                return driver.FindByJQuery<Element>(
                    Framework.jQuery.By.JQuery(Xtype.LABEL.Name + ":contains(" + text + "):visible()").Parent().Find(xtype.Name));
            }
        }

        public static Element XFind(this Element element, Xtype xtype)
        {
            if (xtype == Xtype.FIRSTROW || xtype == Xtype.WINDOW)
            {
                return element.FindByJQuery<Element>(Framework.jQuery.By.JQuery(xtype.Name + ":visible()"));
            }
            return XFind(element, xtype, null);
        }

        public static Element XFind(this Element element, Xtype xtype, string  text)
        {
            if (xtype == Xtype.PANEL ||
                xtype == Xtype.TABPANEL ||
                xtype == Xtype.WINDOW ||
                xtype == Xtype.BUTTON ||
                xtype == Xtype.FIRSTROW ||
                xtype == Xtype.EASMENU ||
                xtype == Xtype.EASMENUSUBITEM ||
                xtype == Xtype.MENUSUBITEMCONTAINER ||
                xtype == Xtype.MENUITEM ||
                xtype == Xtype.COMBOLISTITEM)
            {
                return
                    element.FindByJQuery<Element>(String.IsNullOrEmpty(text)
                        ? Framework.jQuery.By.JQuery(xtype.Name + ":visible()")
                        : Framework.jQuery.By.JQuery(xtype.Name + ":contains(" + text + "):visible()"));
            }
            else
            {
                //if (xtype == Xtype.DATEPICKER ||
                //xtype == Xtype.DATEFIELD ||
                //xtype == Xtype.DICTIONARY ||
                //xtype == Xtype.TEXTFIELD ||
                //xtype == Xtype.COMBOBOX ||
                //xtype == Xtype.CHECKBOX)
                //{
                return element.FindByJQuery<Element>(Framework.jQuery.By.JQuery(Xtype.LABEL.Name + ":contains(" + text + "):visible()").Parent().Find(xtype.Name));
                
            }
        }
    }
}
